Skip to content

Enable and use ML-KEM by default#9732

Open
Frauschi wants to merge 4 commits intowolfSSL:masterfrom
Frauschi:pqc_first
Open

Enable and use ML-KEM by default#9732
Frauschi wants to merge 4 commits intowolfSSL:masterfrom
Frauschi:pqc_first

Conversation

@Frauschi
Copy link
Contributor

@Frauschi Frauschi commented Feb 2, 2026

This PR enables the standardized PQC algorithm ML-KEM (FIPS203) by default.

ML-KEM enabled by default

The ML-KEM algorithm is now enabled by default when building the library. This results in the same behavior as currently passing --enable-mlkem=yes to configure. When a specialized configuration for ML-KEM is required, --enable-mlkem=<desired options> is still possible as normal.

Algorithm changes

Prior to this PR, when ML-KEM has been enabled, all available groups that “contain” ML-KEM have been enabled to be used for the TLS key exchange. This behavior is now changed to the following:

Groups enabled by default: X25519MLKEM768, SECP256R1MLKEM768, SECP384R1MLKEM1024; these are the to-be-standardized hybrid PQ/T combinations from draft-ietf-tls-ecdhe-mlkem. These hybrids are considered “most secure” by the community and are already widely deployed in the web, especially X25519MLKEM768 (e.g., see here).

Currently not enabled by default: all standalone ML-KEM groups (MLKEM512, MLKEM768, MLKEM1024 from draft-ietf-tls-mlkem), as standalone ML-KEM usage is not widely deployed presently and also considered “more dangerous”.

Some “extra” hybrid PQ/T groups with different combinations of ML-KEM and ECC groups, which are defined by the OQS project, are also disabled by default (SECP256R1MLKEM512, SECP384R1MLKEM768, SECP521R1MLKEM1024, X25519MLKEM512).

Next to the default enablement of these groups, the “ preference “ of these groups has also been increased. On the client side, if no specific group for the key share is set, X25519MLKEM768 is now selected as the default one when Curve25519 is enabled. Otherwise, SECP384R1MLKEM1024 is used (if hybrids are not disabled). On the server side, when a client presents more than one key share in its ClientHello, a hybrid one is now also preferred to a traditional-only one.

This now also reflects the behavior of OpenSSL since version 3.5.

Build system changes

To reflect the changes from above, changes in the build system have been performed.

In the autoconf setup, --enable-mlkem is now set to yes by default. Furthermore, the following three new options are added:

  • --enable-pqc-hybrids (on by default): This enables (or disables) the three to-be-standardized hybrid PQ/T combinations.
  • --enable-tls-mlkem-standalone (off by default): This enables the standalone usage of ML-KEM. When enabled, the three standalone groups are advertised in the SupportedGroups extension by the client. When the hybrid groups from above are disabled, then MLKEM1024 is also selected as the default in the key share sent by the client. Otherwise, the hybrids are still ranked higher.
  • --enable-extra-pqc-hybrids (off by default, requires --enable-experimental in addition): This enables the extra PQ/T groups from the OQS project.

In addition to these new options, some dependencies of ML-KEM are now also enabled by default, namely SHA3 (--enable-sha3), SHAKE128 (--enable-shake128) and SHAKE256 (--enable-shake128).

Furthermore, when DTLS 1.3 is enabled, then ClientHello fragmentation (--enable-dtls-frag-ch) is now also enabled by default, as most PQ/T and standalone PQC key shares require fragmentation.

All these options and changes have also been applied to CMake builds and to Zephyr, too.

Runtime changes

The order of the algorithms has been changed in both the SupportedGroups extension sent by the client as well as in the preferredGroup array to rank the three PQ/T hybrids highest. The standalone ML-KEM groups are added based on their achieved security strength into the list, but always before their matching traditional counterparts (to prioritize the PQC groups over traditional ones in case hybrids are disabled).

For DTLS 1.3, the server now also automatically enables ClientHello fragmentation when the client sends a PQ/T or PQC key share (which requires a HelloRetryRequest and a second ClientHello).

For async crypto support, hybrid PQ/T groups are now also handled properly on both the client and server sides (only the ECC part of the hybrid combination is handled asynchronously, as ML-KEM currently lacks async support).

Tests

In the test infrastructure, all the new options are added to be tested. Furthermore, existing tests have been adapted to incorporate the different resource usage for the ML-KEM groups (more memory usage for static memory cases, different fragmentation in case of DTLS, manually set specific groups that are required in some tests, ...).

Moreover, the general testing of PQC and the PQ/T hybrids has been extended to cover all groups and more edge-cases.

Other changes

  • The examples have been updated to incorporate these changes.
  • The two ML-KEM source files wc_mlkem.c and wc_mlkem_poly.c have been updated to fix -Wconversion warnings (now also tested in CI for ML-KEM).
  • When RNG is disabled (--disable-rng), ML-KEM is now usable only with limited functionality. Mainly, key generation and encapsulation are only possible with a given seed. This is also handled in the unit tests.

@Frauschi Frauschi requested review from anhu, dgarske and douzzer February 2, 2026 16:10
@Frauschi Frauschi force-pushed the pqc_first branch 10 times, most recently from cb08c65 to 0040099 Compare February 4, 2026 14:45
Copy link
Contributor

@dgarske dgarske left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is unhappy with some of the implicit casts I think:

                 from wolfcrypt/src/wc_mlkem.c:66:
wolfcrypt/src/wc_mlkem.c: In function 'wc_MlKemKey_MakeKeyWithRandom':
wolfcrypt/src/wc_mlkem.c:519:53: error: conversion to 'long unsigned int' from 'int' may change the sign of the result [-Werror=sign-conversion]
  519 |         e = (sword16*)XMALLOC((k + 1) * k * MLKEM_N * sizeof(sword16),
      |                                                     ^
./wolfssl/wolfcrypt/types.h:790:33: note: in definition of macro 'XMALLOC'
  790 |                 wolfSSL_Malloc((s)))
      |                                 ^
wolfcrypt/src/wc_mlkem.c:560:22: error: conversion from 'int' to 'byte' {aka 'unsigned char'} may change value [-Werror=conversion]
  560 |             buf[0] = k;
      |                      ^
wolfcrypt/src/wc_mlkem.c: In function 'mlkemkey_encapsulate':
wolfcrypt/src/wc_mlkem.c:852:42: error: conversion to 'int' from 'unsigned int' may change the sign of the result [-Werror=sign-conversion]
  852 |         ret = mlkem_get_noise(&key->prf, k, y, e1, e2, r);
      |                                          ^

@Frauschi
Copy link
Contributor Author

Frauschi commented Feb 5, 2026

CI is unhappy with some of the implicit casts I think:

Yeah I already talked to @SparkiDev about these and I still have to fix them (trying to fix the other failing tests first). The ML-KEM source files haven't yet been under test with these conversion checks.

@Frauschi Frauschi force-pushed the pqc_first branch 11 times, most recently from 5e3b1e9 to 644f303 Compare February 11, 2026 15:13
@Frauschi Frauschi force-pushed the pqc_first branch 2 times, most recently from 9e0f2fc to 9124900 Compare February 13, 2026 11:22
dgarske
dgarske previously approved these changes Feb 26, 2026
Copy link
Contributor

@rizlik rizlik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you argument on the comment regarding the HRR, stateless and keyshare?

@Frauschi
Copy link
Contributor Author

Frauschi commented Mar 2, 2026

@julek-wolfssl @rizlik As discussed on Slack, I created #9848 to fix the DTLS HRR group issue discussed above. Once this is merged, I will rebase this PR on top.

@anhu
Copy link
Member

anhu commented Mar 2, 2026

@aisle-analyzer

@Frauschi
Copy link
Contributor Author

Frauschi commented Mar 4, 2026

Jenkins retest this please

@Frauschi
Copy link
Contributor Author

Frauschi commented Mar 4, 2026

I added the commit from #9848 here, too, to ensure it works with the changes here in CI. Once #9848 is merged, a simple rebase will remove that.

With that, all the open DTLS issues should be resolved @julek-wolfssl @rizlik?

The only failure in the PRB-master-job is the already mentioned code-size increase.

julek-wolfssl
julek-wolfssl previously approved these changes Mar 5, 2026
@Frauschi
Copy link
Contributor Author

Frauschi commented Mar 5, 2026

Fixed conflicts to master and squashed final DTLS review commit. no other changes compared to the state that has been approved earlier.

Copy link
Contributor

@douzzer douzzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

succeeded: defaults-gcc-latest quantum-safe-wolfssl-all-crypto-only-intelasm-fips-dev-static-hash-linuxkm-next-insmod check-self-quick check-file-modes check-shell-scripts check-configure quantum-safe-wolfssl-all-cppcheck clang-tidy-all-sp-all sanitizer-all-intelasm-c-fallback-fuzzer all-gcc-c99-asn-original cryptonly-opensslextra-gcc-c99
failed: check-source-text all-check-certs-one-month quantum-safe-wolfssl-all-g++-latest-debug quantum-safe-wolfssl-all-clang++-latest all-crypto-openssl-extra-coexist-smallstack
[check-source-text] [5 of 56] [02dd3a92b0]
    configure...   real 0m9.021s  user 0m5.618s  sys 0m4.465s
8 bit:
./src/tls13.c:
348cb881f1 (<tobias@wolfssl.com> 2026-03-05 17:45:42 +0100 12172)             /* RFC8446 ¶4.6.1: Each ticket must have a unique nonce value.
./src/ssl.c:
5c63adb462 (<tobias@wolfssl.com> 2026-03-05 16:49:30 +0100 3511)     /* RFC8446 ¶4.6.1: Servers MUST NOT use any value greater than

[all-check-certs-one-month] [8 of 56] [02dd3a92b0]
    configure...   real 0m9.029s  user 0m5.701s  sys 0m4.386s
    build...tests/api.c: In function ‘test_dtls13_frag_ch_pq’:
0d15f6f32b (<tobias@wolfssl.com> 2026-02-02 17:02:59 +0100 31313)     int group = WOLFSSL_X25519MLKEM768;
tests/api.c:31313:17: error: ‘WOLFSSL_X25519MLKEM768’ undeclared (first use in this function)
31313 |     int group = WOLFSSL_X25519MLKEM768;
      |                 ^~~~~~~~~~~~~~~~~~~~~~
0d15f6f32b (<tobias@wolfssl.com> 2026-02-02 17:02:59 +0100 31313)     int group = WOLFSSL_X25519MLKEM768;
tests/api.c:31313:17: note: each undeclared identifier is reported only once for each function it appears in
tests/api/test_dtls.c: In function ‘test_dtls_rtx_across_epoch_change’:
0d15f6f32b (<tobias@wolfssl.com> 2026-02-02 17:02:59 +0100 1624)         WOLFSSL_X25519MLKEM768,
tests/api/test_dtls.c:1624:9: error: ‘WOLFSSL_X25519MLKEM768’ undeclared (first use in this function)
 1624 |         WOLFSSL_X25519MLKEM768,
      |         ^~~~~~~~~~~~~~~~~~~~~~
0d15f6f32b (<tobias@wolfssl.com> 2026-02-02 17:02:59 +0100 1624)         WOLFSSL_X25519MLKEM768,
tests/api/test_dtls.c:1624:9: note: each undeclared identifier is reported only once for each function it appears in
make[2]: *** [Makefile:10467: tests/api/unit_test-test_dtls.o] Error 1
make[2]: *** Waiting for unfinished jobs....
make[2]: *** [Makefile:9795: tests/unit_test-api.o] Error 1
make[1]: *** [Makefile:11305: all-recursive] Error 1
make: *** [Makefile:6198: all] Error 2
   real 0m9.056s  user 1m40.950s  sys 0m4.854s
    scenario started 2026-03-05T18:57:31.009698Z, real elapsed 0m18.095518s
    all-check-certs-one-month fail_build
    failed config: 'EXTRA_CPPFLAGS=-Werror' '--srcdir' '.' '--disable-jobserver' '--enable-option-checking=fatal' '--enable-all' '--enable-acert' '--enable-dtls13' '--enable-dtls-mtu' '--enable-dtls-frag-ch' '--enable-dtlscid' '--enable-quic' '--with-sys-crypto-policy' '--enable-experimental' '--enable-kyber=yes,original' '--enable-lms' '--enable-xmss' '--enable-dilithium' '--enable-dual-alg-certs' '--disable-qt' '--disable-dual-alg-certs' 'CFLAGS=-DTEST_ALWAYS_RUN_TO_END -UOPENSSL_COMPATIBLE_DEFAULTS -DWOLFSSL_DEBUG_CERTIFICATE_LOADS -DNO_WOLFSSL_CIPHER_SUITE_TEST -DWOLFSSL_OLD_PRIME_CHECK -pedantic -Wdeclaration-after-statement -Wnull-dereference -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE'


[quantum-safe-wolfssl-all-g++-latest-debug] [10 of 56] [02dd3a92b0]
    configure...   real 0m21.879s  user 0m12.389s  sys 0m11.103s
    build...tests/api/test_dtls.c: In function ‘int test_dtls_rtx_across_epoch_change()’:
0d15f6f32b (<tobias@wolfssl.com> 2026-02-02 17:02:59 +0100 1624)         WOLFSSL_X25519MLKEM768,
tests/api/test_dtls.c:1624:9: error: ‘WOLFSSL_X25519MLKEM768’ was not declared in this scope
 1624 |         WOLFSSL_X25519MLKEM768,
      |         ^~~~~~~~~~~~~~~~~~~~~~
make[2]: *** [Makefile:10467: tests/api/unit_test-test_dtls.o] Error 1
make[2]: *** Waiting for unfinished jobs....
tests/api.c: In function ‘int test_dtls13_frag_ch_pq()’:
0d15f6f32b (<tobias@wolfssl.com> 2026-02-02 17:02:59 +0100 31313)     int group = WOLFSSL_X25519MLKEM768;
tests/api.c:31313:17: error: ‘WOLFSSL_X25519MLKEM768’ was not declared in this scope
31313 |     int group = WOLFSSL_X25519MLKEM768;
      |                 ^~~~~~~~~~~~~~~~~~~~~~
make[2]: *** [Makefile:9795: tests/unit_test-api.o] Error 1
make[1]: *** [Makefile:11305: all-recursive] Error 1
make: *** [Makefile:6198: all] Error 2
   real 0m11.358s  user 1m50.414s  sys 0m6.691s
    scenario started 2026-03-05T18:57:50.741030Z, real elapsed 0m33.253833s
    quantum-safe-wolfssl-all-g++-latest-debug fail_build
    failed config: 'EXTRA_CPPFLAGS=-Werror' '--srcdir' '.' '--disable-jobserver' '--enable-option-checking=fatal' '--enable-all' '--enable-acert' '--enable-dtls13' '--enable-dtls-mtu' '--enable-dtls-frag-ch' '--enable-dtlscid' '--enable-quic' '--with-sys-crypto-policy' '--enable-debug' '--enable-debug-trace-errcodes' '--enable-sp-math-all' '--enable-experimental' '--enable-kyber=yes,original' '--enable-lms' '--enable-xmss' '--enable-dilithium' '--enable-dual-alg-certs' '--disable-qt' 'CC=g++-16' 'CFLAGS=-DTEST_ALWAYS_RUN_TO_END' 'CPPFLAGS=-DNO_WOLFSSL_CIPHER_SUITE_TEST -DWOLFSSL_OLD_PRIME_CHECK'


[all-crypto-openssl-extra-coexist-smallstack] [17 of 56] [02dd3a92b0]
    configure...   real 0m12.438s  user 0m7.331s  sys 0m6.099s
    build...tests/api/test_dtls.c: In function ‘test_dtls_rtx_across_epoch_change’:
0d15f6f32b (<tobias@wolfssl.com> 2026-02-02 17:02:59 +0100 1624)         WOLFSSL_X25519MLKEM768,
tests/api/test_dtls.c:1624:9: error: ‘WOLFSSL_X25519MLKEM768’ undeclared (first use in this function)
 1624 |         WOLFSSL_X25519MLKEM768,
      |         ^~~~~~~~~~~~~~~~~~~~~~
0d15f6f32b (<tobias@wolfssl.com> 2026-02-02 17:02:59 +0100 1624)         WOLFSSL_X25519MLKEM768,
tests/api/test_dtls.c:1624:9: note: each undeclared identifier is reported only once for each function it appears in
make[2]: *** [Makefile:10467: tests/api/unit_test-test_dtls.o] Error 1
make[2]: *** Waiting for unfinished jobs....
tests/api.c: In function ‘test_dtls13_frag_ch_pq’:
0d15f6f32b (<tobias@wolfssl.com> 2026-02-02 17:02:59 +0100 31313)     int group = WOLFSSL_X25519MLKEM768;
tests/api.c:31313:17: error: ‘WOLFSSL_X25519MLKEM768’ undeclared (first use in this function)
31313 |     int group = WOLFSSL_X25519MLKEM768;
      |                 ^~~~~~~~~~~~~~~~~~~~~~
0d15f6f32b (<tobias@wolfssl.com> 2026-02-02 17:02:59 +0100 31313)     int group = WOLFSSL_X25519MLKEM768;
tests/api.c:31313:17: note: each undeclared identifier is reported only once for each function it appears in
make[2]: *** [Makefile:9795: tests/unit_test-api.o] Error 1
make[1]: *** [Makefile:11305: all-recursive] Error 1
make: *** [Makefile:6198: all] Error 2
   real 0m13.611s  user 2m8.495s  sys 0m5.029s
    scenario started 2026-03-05T19:04:17.613591Z, real elapsed 0m26.062712s
    all-crypto-openssl-extra-coexist-smallstack fail_build
    failed config: 'EXTRA_CPPFLAGS=-Werror' '--srcdir' '.' '--disable-jobserver' '--enable-option-checking=fatal' '--enable-all' '--enable-acert' '--enable-dtls13' '--enable-dtls-mtu' '--enable-dtls-frag-ch' '--enable-dtlscid' '--enable-quic' '--with-sys-crypto-policy' '--enable-experimental' '--enable-kyber=yes,original' '--enable-lms' '--enable-xmss' '--enable-dilithium' '--enable-dual-alg-certs' '--disable-qt' '--disable-opensslall' '--enable-opensslcoexist' '--disable-all-osp' '--enable-wpas' '--disable-quic' '--enable-smallstack' '--enable-smallstackcache' 'CFLAGS=-DTEST_ALWAYS_RUN_TO_END -DOPENSSL_COMPATIBLE_DEFAULTS' 'CPPFLAGS=-DNO_WOLFSSL_CIPHER_SUITE_TEST -DWOLFSSL_OLD_PRIME_CHECK -pedantic -Wdeclaration-after-statement -Wnull-dereference -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE'

Frauschi added 4 commits March 6, 2026 12:11
* Enable ML-KEM by default in build systems (autoconf and CMake)
* Only allow three to-be-standardized hybrid PQ/T combinations by
  default
* Use X25519MLKEM768 as the default KeyShare in the ClientHello (if user
  does not override that). When Curve25519 is disabled, then either
  WOLFSSL_SECP384R1MLKEM1024 or WOLFSSL_SECP256R1MLKEM768 is used as
  default depending on the ECC configuration
* Disable standalone ML-KEM in supported groups by default (enable with
  --enable-tls-mlkem-standalone)
* Disable extra OQS-based hybrid PQ/T curves by default and gate
  behind --enable-experimental (enable with --enable-extra-pqc-hybrids)
* Reorder the SupportedGroups extension to reflect the preferences
* Reorder the preferredGroup array to also reflect the same preferences
* Enable DTLS1.3 ClientHello fragmentation by default when both DTLS1.3
  and ML-KEM are enabled
* Fix memory leak in TLS server PQC handling in case of ECH
* Ensure PQ/T hybrids are properly tested in unit tests
* Increase static memory buffer sizes to account for ML-KEM heap usage
* Add async support for ML-KEM hybrids
Also fix minor problems found with these tests
* fix -Wconversion warnings
* allow APIs without RNG usage in case WC_NO_RNG is defined
@Frauschi
Copy link
Contributor Author

Frauschi commented Mar 6, 2026

failed: check-source-text all-check-certs-one-month quantum-safe-wolfssl-all-g++-latest-debug quantum-safe-wolfssl-all-clang++-latest all-crypto-openssl-extra-coexist-smallstack

Should all be fixed now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For This Release Release version 5.9.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants